package com.oauth2.server.config;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.oauth2.config.annotation.configurers.ClientDetailsServiceConfigurer;
import org.springframework.security.oauth2.config.annotation.web.configuration.AuthorizationServerConfigurerAdapter;
import org.springframework.security.oauth2.config.annotation.web.configuration.EnableAuthorizationServer;
import org.springframework.security.oauth2.config.annotation.web.configurers.AuthorizationServerEndpointsConfigurer;
import org.springframework.security.oauth2.config.annotation.web.configurers.AuthorizationServerSecurityConfigurer;
import org.springframework.security.oauth2.provider.token.TokenStore;
import org.springframework.security.oauth2.provider.token.store.JwtAccessTokenConverter;

/**
 * @Author: zj
 * @Description: OAuth2.0 安全认证配置
 * @Date: Created in 13:23 2020/11/20
 * @Modified By:
 */
@Configuration
@EnableAuthorizationServer
public class OAuth2Config extends AuthorizationServerConfigurerAdapter {

    /**
     * 密码加密器
     */
    @Autowired
    public PasswordEncoder passwordEncoder;

    @Autowired
    public UserDetailsService kiteUserDetailsService;

    @Autowired
    private AuthenticationManager authenticationManager;

    @Autowired
    private TokenStore redisTokenStore;

    @Override
    public void configure(final AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
        /**
         * redis token 方式
         */

        endpoints
                //调用此方法才能支持 password 模式
                .authenticationManager(authenticationManager)
                //设置用户验证服务
                .userDetailsService(kiteUserDetailsService)
                // 指定 token 的存储方式 redis
                .tokenStore(redisTokenStore);

    }

    /**
     * 定义各个端的约束条件
     * authorizedGrantTypes 可以包括如下几种设置中的一种或多种：
     *
     * - authorization_code：授权码类型。
     * - implicit：隐式授权类型。
     * - password：资源所有者（即用户）密码类型。
     * - client_credentials：客户端凭据（客户端ID以及Key）类型。
     * - refresh_token：通过以上授权获得的刷新令牌来获取新的令牌。
     * @param clients
     * @throws Exception
     */
    @Override
    public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
        clients.inMemory()
                // client_id
                .withClient("order-client")
                // client_secret
                .secret(passwordEncoder.encode("order-secret-8888"))
                // 授权方式
                .authorizedGrantTypes("refresh_token", "authorization_code", "password")
                //token 的有效期
                .accessTokenValiditySeconds(3600)
                //scopes：用来限制客户端访问的权限，在换取的 token 的时候会带上 scope 参数，
                // 只有在 scopes 定义内的，才可以正常换取 token
                .scopes("all")
                .and()
                .withClient("user-client")
                .secret(passwordEncoder.encode("user-secret-8888"))
                .authorizedGrantTypes("refresh_token", "authorization_code", "password")
                .accessTokenValiditySeconds(3600)
                .scopes("all");
    }

    @Override
    public void configure(AuthorizationServerSecurityConfigurer security) throws Exception {
        //允许客户端访问 OAuth2 授权接口，否则请求 token 会返回 401
        security.allowFormAuthenticationForClients();
        //允许已授权用户访问 checkToken 接口
        security.checkTokenAccess("isAuthenticated()");
        //允许已授权用户访问 获取Token 接口
        security.tokenKeyAccess("isAuthenticated()");
    }
}
